/*
 * Copyright (C) 2011 JiangHongTiao
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package sk.lieskove.jianghongtiao.multithreaddownloader.request;

import org.apache.log4j.Logger;
import sk.lieskove.jianghongtiao.multithreaddownloader.MultiThreadDownloadManager;
import sk.lieskove.jianghongtiao.multithreaddownloader.MultiThreadDownloader;
import sk.lieskove.jianghongtiao.multithreaddownloader.document.RemoteDocument;
import sk.lieskove.jianghongtiao.multithreaddownloader.request.converter.Converter;
import sk.lieskove.jianghongtiao.multithreaddownloader.request.exceptions.NotEnoughParametersException;
import sk.lieskove.jianghongtiao.multithreaddownloader.request.exceptions.ParameterNotAllowedException;
import sk.lieskove.jianghongtiao.multithreaddownloader.request.params.Param;
import sk.lieskove.jianghongtiao.multithreaddownloader.request.params.ParamImpl;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;


/**
 * Date of create: May 20, 2011
 * 
 * Simple service for GET web server queries.
 *
 * @author JiangHongTiao <jjurco.sk_gmail.com>
 * @version 2011.0520
 */
public abstract class PageRequestBuilder implements PageRequest {

    private String urlEncoding = "UTF-8";
    private final Map<String, Converter> converters = new HashMap<String, Converter>();
    private final Map<String, Param> params = new HashMap<String, Param>();
    private Logger log = Logger.getLogger(this.getClass().getName());
    private String serviceUrl;
    private String[] obligatory = null;
    private String[] oneOf = null;
    private MultiThreadDownloadManager downloadManager = MultiThreadDownloader.getInstance();

    /**
     * @param paramName name of the parameter to insert
     * @param paramValue value for the parameter
     * @throws ParameterNotAllowedException if this parameter is not supported by this service
     */
    @Override
    public void putParam(String paramName, String paramValue) throws
            ParameterNotAllowedException {
        if (converters.containsKey(paramName)) {
            Converter c = converters.get(paramName);
            params.put(paramName, new ParamImpl(paramName, paramValue, c));
            return;
        }
        throw new ParameterNotAllowedException("Parameter " + paramName
                + " is not allowed for this service!");
    }

    protected void setObligatory(String[] obligatory) {
        this.obligatory = obligatory;
    }

    protected void setOneOf(String[] oneOf) {
        this.oneOf = oneOf;
    }

    @Override
    public String removeParam(String name) {
        String result = params.get(name).getValue();
        return result;
    }

    private String makeUrl() {
        String result = serviceUrl + "?";
        try {
            for (Iterator it = params.keySet().iterator(); it.hasNext();) {
                String param = (String) it.next();
                Param p = params.get(param);
                result += p.getParamName() + "=" + URLEncoder.encode(
                        p.getValue(), urlEncoding) + "&";
            }
        } catch (UnsupportedEncodingException ex) {
            log.error("Encoding is not correct! System does not know this encoding!"+ex);
        }
        return result.substring(0, result.length()-1);
    }

    @Override
    public RemoteDocument getAnswer() throws NotEnoughParametersException{
        if(!allNecessaryParamsPresent()){
            throw new NotEnoughParametersException("Required parameters are not "
                    + "present. Obligatory parameters are: " + Arrays.toString(
                    obligatory) + ". Or one of "+Arrays.toString(oneOf) + 
                    " is required");
        }
        RemoteDocument downloadPage = null;
        try {
            UUID uuid = downloadManager.addLink(new URL(makeUrl()));
            downloadPage = downloadManager.getDownload(uuid);
        } catch (MalformedURLException ex) {
            log.error("Error thrown while trying download file: '"
                    + makeUrl() + "'. URL has malformed format", ex);
        }
        return downloadPage;
    }

    protected void setServiceUrl(String url) {
        serviceUrl = url;
    }

    protected void setConverters(Map<String, Converter> converters) {
        this.converters.putAll(converters);
    }

    private boolean allNecessaryParamsPresent() {
        for(String obl : obligatory){
            if(!params.containsKey(obl)){
                log.error("Required parameter '" + obl + "' is not present!");
                return false;
            }
        }
        if(oneOf.length == 0){
            return true;
        }
        int count = 0;
        for(String one: oneOf){
            if(params.containsKey(one)){
                log.info("Optional parameter '" + one + "' is not present.");
                count++;
            }
        }
        return count > 0;
    }
    
    @Override
    public Map<String, Param> getParams(){
        return Collections.unmodifiableMap(params);
    }

    public String getUrlEncoding() {
        return urlEncoding;
    }

    public void setUrlEncoding(String urlEncoding) {
        this.urlEncoding = urlEncoding;
    }

}
